
#region Imported Namespaces
using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Web;
using System.Web.UI;
using System.Web.UI.Design;
using System.Web.UI.WebControls;
#endregion

namespace PHSRAG.WebControls
{
	#region Class ActivityIndicator
	/// <summary>
	/// The ActivityIndicator provides a mechanism to provide visual feedback while a lengthy operation is in progress.
	/// An assumption is made that the form does NOT declare its own onsubmit event handler.
	/// </summary>
	/// <remarks>
	/// <p>Any page which would like to display the activity indicator should include this control on the page.</p>
	/// <p>Typically, the container of this control is a HTML table. We would like that container and the ActivityIndicator
	/// not to occupy any client real-estate until such time that the ActivityIndicator has something to display. This is
	/// controlled by setting the style on both the container and the table created during control-tree generation to render
	/// them invisible. When it is time to show something, both the container and the table are rendered visible.
	/// Client-side scrpting is used to do this.</p>
	/// <p>To enable the ActivityIndicator to control the visibility of its conatiner, the <b>ContainerID</b> property
	/// should be set to the ID of its container (as shown in the example below).</p>
	/// </remarks>
	/// <example>
	/// The following example shows how the page would use this control. The registration of the control can be done
	/// simply by dropping the control on the page in design mode.
	/// <code>
	/// <![CDATA[
	/// <%@ Register TagPrefix="cc1" Namespace="PHSRAG.WebControls" Assembly="PHSRAG.WebControls" %>
	/// ...
	/// <body>
	/// ...
	/// <table id="dynamicTable" style="visibility:hidden;display:none">
	///		<tr><td><cc1:ActivityIndicator id="activityIndicator" runat="server" ContainerID="dynamicTable" /></td></tr>
	///	</table>
	/// ...
	/// </body>
	/// ]]>
	/// </code>
	/// </example>
	[ToolboxData("<{0}:ActivityIndicator runat=server />"), Designer(typeof(ActivityIndicatorDesigner))]
	public class ActivityIndicator : System.Web.UI.WebControls.WebControl, INamingContainer
	{
		#region Constants
		private const string	TableID = "activityIndicatorTable";
		private const string	DefaultMessage = "Please wait...";
		#endregion

		#region Instance Variables
		private string	containerID;
		private string	message = DefaultMessage;
		private Image	clock;
		#endregion

		#region Public Properties
		/// <summary>
		/// Get/Set the ID of the conatainer.
		/// </summary>
		public string ContainerID
		{
			set { containerID = value; }
		}

		/// <summary>
		/// Get/Set the message displayed during activity period.
		/// </summary>
		[Bindable(true), Category("Appearance")] 
		public string Message 
		{
			get { EnsureChildControls(); return message; }
			set	{ EnsureChildControls(); message = value;	}
		}
		#endregion

		#region Overrides
		/// <summary>
		/// Create all controls that will be on the Header. Notice that the creation is more than just instantiating
		/// the controls. It also involves in generating the underlying HTML so that the controls shows up per the need.
		/// </summary>
		protected override void CreateChildControls()
		{
			CreateControls();
			GenerateControlTree();
		}

		/// <summary>
		/// Load child controls and register the client-side scripts.
		/// </summary>
		/// <param name="e">Argument associated with the event</param>
		protected override void OnLoad(EventArgs e)
		{
			base.OnLoad(e);
			EnableViewState = false;
			EnsureChildControls();
			RegisterScripts();
		}
		#endregion

		#region Private Methods
		/// <summary>
		/// Create all controls in the ActivityIndicator.
		/// </summary>
		private void CreateControls()
		{
			clock = new Image();
			clock.ID = "clock";
            clock.ImageUrl = VirtualPathUtility.ToAbsolute("~/Images/clock-icon.gif");
        }

		/// <summary>
		/// Setup the control tree to reflect the actual (and exact) layout of the controls within the ActivityIndicator.
		/// </summary>
		private void GenerateControlTree()
		{
			Controls.Add(new LiteralControl(string.Format(
				"<table class=\"ActivityIndicator\" align=\"center\" id=\"{0}\" style=\"visibility:hidden;display:none;\" class=\"ActivityIndicator\">\n" +
				"\t<tr>\n" +
				"\t\t<td width=\"5%\" align=\"right\">", TableID)));
			Controls.Add(clock);
			Controls.Add(new LiteralControl("</td>\n<td width=\"95%\" align=\"left\" valign=\"middle\">"));
			Controls.Add(new LiteralControl(message));
			Controls.Add(new LiteralControl(
				"</td>\n" +
				"\t</tr>\n" +
				"</table>"));
		}

		/// <summary>
		/// Register the client-side javascript functions to show visual feedback.
		/// </summary>
		/// <remarks>
		/// <p>The visual feedback is provided as follows:</p>
		/// <p>The __doPostBack handler code (generated by ASP.NET) is stored away in a local variable, and is replaced
		/// by our own javascript function (named "NewPostBack"). This replacement function simply calls on another
		/// javascript function "ProvideFeedback" (which we create) and then calls the saved version of the
		/// __doPostBack function. This is done so that our feedback function fires BEFORE any postback occurs.
		/// The ProvideFeedback function simply displays the current time and any message that have been set (via the
		/// Message property), and sets itself as the handler for a timer which fires every second.
		/// As long as the page is still being processed on the server, the client-side code continues to run, updating
		/// the time display. As soon as the page comes back from the server, the display vanishes (since the page is
		/// reloaded).</p>
		/// <p>Typically we would like this control not to occupy any client real-estate until such time that the control
		/// has something to display. This is controlled by setting the style on both the container (in HTML) and the table
		/// created during control-tree generation to render them invisible. When it is time to show something, both the
		/// container and the table are rendered visible. Client-side scrpting is used to do this.</p>
		/// </remarks>
		private void RegisterScripts()
		{
			const string scriptBlockKey = "FeedBackScript";
            if (!Page.ClientScript.IsClientScriptBlockRegistered(scriptBlockKey))
                Page.ClientScript.RegisterClientScriptBlock(GetType(), scriptBlockKey, string.Format(
					"<script type=\"text/javascript\">\n" +
					"function ProvideFeedback()\n" +
					"{{\n" +
					"\tvar userDefinedValidationResult = true;\n" +
					"\ttry\n" +
					"\t{{\n" +
                    "\t\tif ( typeof(UserDefinedValidation) == 'function' )\n" +
					"\t\t\tuserDefinedValidationResult = UserDefinedValidation();\n" +
                    "\t\telse if ( typeof(IsDirty) == 'function' )\n" +
                    "\t\t\tuserDefinedValidationResult = !IsDirty();\n" +
                    "\t\tif ( !userDefinedValidationResult )\n" +
                    "\t\t\treturn false;\n" +
                    "\t}}\n" +
					"\tcatch(e)\n" +
					"\t{{\n" +
					"\t}}\n" +
                    "\tdocument.body.style.cursor='progress';\n" +
					"\tvar t = document.getElementById('{0}');\n" +
					"\tif (t != null) {{ t.style.visibility = 'visible'; t.style.display = 'block'; }}\n" +
					"\tt = document.getElementById('{1}');\n" +
					"\tif (t != null) {{ t.style.visibility = 'visible'; t.style.display = 'block'; }}\n" +
					"}}\n\n" +
					"function EndFeedback()\n" +
					"{{\n" +
					"\tdocument.body.style.cursor = 'default';\n" +
					"\tvar t = document.getElementById('dynamicTable');\n" +
					"\tif (t != null) {{ t.style.visibility = 'hidden'; t.style.display = 'none'; }}\n" +
					"\tt = document.getElementById('activityIndicatorTable');\n" +
					"\tif (t != null) {{ t.style.visibility = 'hidden'; t.style.display = 'none'; }}\n" +
					"}}\n" +
					"\n" +
					"</script>\n" +
					"<script type=\"text/javascript\">\n" +
					"<!--\n" +
					"var oldPostBack;\n\n" +
					"function window.onload()\n" +
					"{{\n" +
					"\ttry\n" +
					"\t{{\n" +
					"\t\toldPostBack = __doPostBack;\n" +
					"\t\t__doPostBack = NewPostBack;\n" +
					"\t}}\n" +
					"\tcatch(e)\n" +
					"\t{{\n" +
					"\t}}\n" +
					"}}\n\n" +
					"function NewPostBack(eventTarget, eventArgument)\n" +
					"{{\n" +
					"\tProvideFeedback();\n" +
					"\toldPostBack(eventTarget, eventArgument);\n" +
					"}}\n\n" +
					"// -->\n" +
					"</script>\n", containerID, TableID));

            Page.ClientScript.RegisterOnSubmitStatement(GetType(), "submit", "ProvideFeedback();");
		}
		#endregion
	}
	#endregion

	#region Class ActivityIndicatorDesigner
	/// <summary>
	/// Designer associated with the ActivityIndicator control.
	/// </summary>
	/// <remarks>
	/// This class cannot be used directly.
	/// </remarks>
	public class ActivityIndicatorDesigner : ControlDesigner
	{
		#region Overrides
		/// <summary>
		/// Since the ActivityIndicator control does not have any design-time visibility, the designer provides
		/// the type name of that control and the name of the instance.
		/// </summary>
		/// <returns>HTML representation of the Annunciator control</returns>
		public override string GetDesignTimeHtml()
		{
			return CreatePlaceHolderDesignTimeHtml();
		}
		#endregion
	}
	#endregion
}

// --- EOF ---
